/******************************************************************************* * Copyright (c) 2012,2015 EclipseSource and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Holger Staudacher - initial API and implementation * Dragos Dascalita - disbaled autodiscovery * Lars Pfannenschmidt - made WADL generation configurable * Ivan Iliev - added ServletConfiguration handling, Optimized Performance ******************************************************************************/ package com.eclipsesource.jaxrs.publisher.internal; import java.util.ArrayList; import java.util.Dictionary; import java.util.List; import java.util.Map; import javax.servlet.ServletException; import javax.ws.rs.core.Application; import org.osgi.service.http.HttpContext; import org.osgi.service.http.HttpService; import org.osgi.service.http.NamespaceException; import com.eclipsesource.jaxrs.publisher.ApplicationConfiguration; import com.eclipsesource.jaxrs.publisher.ServletConfiguration; import com.eclipsesource.jaxrs.publisher.internal.ServiceContainer.ServiceHolder; @SuppressWarnings( "rawtypes" ) public class JerseyContext { private final RootApplication application; private final HttpService httpService; private final ServletContainerBridge servletContainerBridge; private final ResourcePublisher resourcePublisher; private volatile boolean isApplicationRegistered; private ServletConfiguration servletConfiguration; private String rootPath; private ServiceContainer applicationConfigurations; public JerseyContext( HttpService httpService, Configuration configuration, ServletConfiguration servletConfiguration, ServiceContainer applicationConfigurations ) { this.httpService = httpService; this.rootPath = configuration.getRoothPath(); this.application = new RootApplication(); this.applicationConfigurations = applicationConfigurations; applyApplicationConfigurations( applicationConfigurations ); this.servletContainerBridge = new ServletContainerBridge( application ); this.servletConfiguration = servletConfiguration; this.resourcePublisher = new ResourcePublisher( servletContainerBridge, configuration.getPublishDelay() ); } void applyApplicationConfigurations( ServiceContainer applicationConfigurations ) { getRootApplication().addProperties( new DefaultApplicationConfiguration().getProperties() ); ServiceHolder[] services = applicationConfigurations.getServices(); for( ServiceHolder serviceHolder : services ) { Object service = serviceHolder.getService(); if( service instanceof ApplicationConfiguration ) { Map<String, Object> properties = ( ( ApplicationConfiguration )service ).getProperties(); if( properties != null ) { getRootApplication().addProperties( properties ); } } } } public void addResource( Object resource ) { getRootApplication().addResource( resource ); registerServletWhenNotAlreadyRegistered(); resourcePublisher.schedulePublishing(); } public void updateConfiguration( Configuration configuration ) { resourcePublisher.setPublishDelay( configuration.getPublishDelay() ); String oldRootPath = this.rootPath; this.rootPath = configuration.getRoothPath(); handleRootPath( oldRootPath ); applyApplicationConfigurations( this.applicationConfigurations ); handeRepublish(); } private void handleRootPath( String oldRootPath ) { // if rootPath has changed and we already have registered our servlet and we need to refresh it if( isApplicationRegistered && !oldRootPath.equals( rootPath ) ) { refreshServletRegistration( oldRootPath ); } } public void updateAppConfiguration( ServiceContainer applicationConfigurations ) { this.applicationConfigurations = applicationConfigurations; applyApplicationConfigurations( this.applicationConfigurations ); handeRepublish(); } public void updateServletConfiguration( ServletConfiguration servletConfiguration ) { boolean isServletUpdateRequired = this.servletConfiguration != servletConfiguration; this.servletConfiguration = servletConfiguration; // if servletConfiguration object has changed and we already have a servlet - refresh it if( isApplicationRegistered && isServletUpdateRequired ) { refreshServletRegistration( rootPath ); } handeRepublish(); } private void handeRepublish() { // if application has been marked dirty and we have registered resources - we will republish if( isApplicationRegistered ) { resourcePublisher.schedulePublishing(); } } private void refreshServletRegistration( String pathToUnregister ) { synchronized( servletContainerBridge ) { safeUnregister( pathToUnregister ); } registerServletWhenNotAlreadyRegistered(); } void registerServletWhenNotAlreadyRegistered() { if( !isApplicationRegistered ) { isApplicationRegistered = true; registerApplication(); } } private void registerApplication() { ClassLoader loader = getContextClassloader(); setContextClassloader(); try { registerServlet(); } catch( ServletException shouldNotHappen ) { throw new IllegalStateException( shouldNotHappen ); } catch( NamespaceException shouldNotHappen ) { throw new IllegalStateException( shouldNotHappen ); } finally { resetContextClassloader( loader ); } } private ClassLoader getContextClassloader() { return Thread.currentThread().getContextClassLoader(); } private void setContextClassloader() { Thread.currentThread().setContextClassLoader( getClass().getClassLoader() ); } private void registerServlet() throws ServletException, NamespaceException { ClassLoader original = getContextClassloader(); try { Thread.currentThread().setContextClassLoader( Application.class.getClassLoader() ); httpService.registerServlet( rootPath, servletContainerBridge, getInitParams(), getHttpContext() ); } finally { resetContextClassloader( original ); } } private Dictionary getInitParams() { if( servletConfiguration != null ) { return servletConfiguration.getInitParams( httpService, rootPath ); } return null; } private HttpContext getHttpContext() { if( servletConfiguration != null ) { return servletConfiguration.getHttpContext( httpService, rootPath ); } return null; } private void resetContextClassloader( ClassLoader loader ) { Thread.currentThread().setContextClassLoader( loader ); } public void removeResource( Object resource ) { getRootApplication().removeResource( resource ); unregisterServletWhenNoResourcePresents(); resourcePublisher.schedulePublishing(); } private void unregisterServletWhenNoResourcePresents() { if( !getRootApplication().hasResources() && isApplicationRegistered ) { // unregistering while jersey context is being reloaded can lead to many exceptions resourcePublisher.cancelPublishing(); synchronized( servletContainerBridge ) { safeUnregister( this.rootPath ); } } } public List<Object> eliminate() { resourcePublisher.cancelPublishing(); resourcePublisher.shutdown(); if( isApplicationRegistered ) { synchronized( servletContainerBridge ) { safeUnregister( this.rootPath ); } } return new ArrayList<Object>( getRootApplication().getResources() ); } void safeUnregister( String rootPath ) { try { // this should call destroy on our servlet container httpService.unregister( rootPath ); } catch( Exception jerseyShutdownException ) { // do nothing because jersey sometimes throws an exception during shutdown } isApplicationRegistered = false; } // For testing purpose RootApplication getRootApplication() { return application; } }